package com.teamfractal.fracdustry.common.fluid.init;

import com.google.common.collect.ImmutableList;
import com.google.common.collect.Lists;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.sounds.SoundEvent;
import net.minecraft.sounds.SoundEvents;
import net.minecraft.tags.FluidTags;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.level.BlockGetter;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.LevelAccessor;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.Blocks;
import net.minecraft.world.level.block.BucketPickup;
import net.minecraft.world.level.block.RenderShape;
import net.minecraft.world.level.block.state.BlockBehaviour;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.block.state.StateDefinition;
import net.minecraft.world.level.block.state.properties.BlockStateProperties;
import net.minecraft.world.level.block.state.properties.IntegerProperty;
import net.minecraft.world.level.material.FlowingFluid;
import net.minecraft.world.level.material.FluidState;
import net.minecraft.world.level.pathfinder.PathComputationType;
import net.minecraft.world.level.storage.loot.LootContext;
import net.minecraft.world.phys.shapes.CollisionContext;
import net.minecraft.world.phys.shapes.Shapes;
import net.minecraft.world.phys.shapes.VoxelShape;

import javax.annotation.Nonnull;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
import java.util.Random;

public class RedactedLiquidBlock extends Block implements BucketPickup {
    protected final FlowingFluid fluid;
    public static final IntegerProperty LEVEL = BlockStateProperties.LEVEL;
    private final List<FluidState> stateCache;
    public static final VoxelShape STABLE_SHAPE = Block.box(0.0D, 0.0D, 0.0D, 16.0D, 8.0D, 16.0D);
    public static final ImmutableList<Direction> POSSIBLE_FLOW_DIRECTIONS = ImmutableList.of(Direction.DOWN, Direction.SOUTH, Direction.NORTH, Direction.EAST, Direction.WEST);

    public RedactedLiquidBlock(java.util.function.Supplier<? extends FlowingFluid> p_54694_, BlockBehaviour.Properties p_54695_) {
        super(p_54695_);
        this.fluid = null;
        this.stateCache = Lists.newArrayList();
        this.registerDefaultState(this.stateDefinition.any().setValue(LEVEL, 0));
        this.supplier = p_54694_;
    }


    @Override
    @Nonnull
    public VoxelShape getCollisionShape(@Nonnull BlockState p_54760_, @Nonnull BlockGetter p_54761_, @Nonnull BlockPos p_54762_, CollisionContext p_54763_) {
        return p_54763_.isAbove(STABLE_SHAPE, p_54762_, true) && p_54760_.getValue(LEVEL) == 0 && p_54763_.canStandOnFluid(p_54761_.getFluidState(p_54762_.above()), this.getFluid()) ? STABLE_SHAPE : Shapes.empty();
    }

    @Override
    public boolean isRandomlyTicking(BlockState p_54732_) {
        return p_54732_.getFluidState().isRandomlyTicking();
    }

    @Override
    public void randomTick(BlockState p_54740_, @Nonnull ServerLevel p_54741_, @Nonnull BlockPos p_54742_, @Nonnull Random p_54743_) {
        p_54740_.getFluidState().randomTick(p_54741_, p_54742_, p_54743_);
    }

    @Override
    public boolean propagatesSkylightDown(@Nonnull BlockState p_54745_, @Nonnull BlockGetter p_54746_, @Nonnull BlockPos p_54747_) {
        return false;
    }

    @Override
    public boolean isPathfindable(@Nonnull BlockState p_54704_, @Nonnull BlockGetter p_54705_, @Nonnull BlockPos p_54706_, @Nonnull PathComputationType p_54707_) {
        return !this.getFluid().is(FluidTags.LAVA);
    }

    @Override
    @Nonnull
    public FluidState getFluidState(BlockState p_54765_) {
        int i = p_54765_.getValue(LEVEL);
        if (!fluidStateCacheInitialized) initFluidStateCache();
        return this.stateCache.get(Math.min(i, 8));
    }

    @Override
    public boolean skipRendering(@Nonnull BlockState p_54716_, BlockState p_54717_, @Nonnull Direction p_54718_) {
        return p_54717_.getFluidState().getType().isSame(this.getFluid());
    }

    @Override
    @Nonnull
    public RenderShape getRenderShape(@Nonnull BlockState p_54738_) {
        return RenderShape.INVISIBLE;
    }

    @Override
    @Nonnull
    public List<ItemStack> getDrops(@Nonnull BlockState p_54720_, @Nonnull LootContext.Builder p_54721_) {
        return Collections.emptyList();
    }

    @Override
    @Nonnull
    public VoxelShape getShape(@Nonnull BlockState p_54749_, @Nonnull BlockGetter p_54750_, @Nonnull BlockPos p_54751_, @Nonnull CollisionContext p_54752_) {
        return Shapes.empty();
    }

    @Override
    public void onPlace(@Nonnull BlockState p_54754_, @Nonnull Level p_54755_, @Nonnull BlockPos p_54756_, @Nonnull BlockState p_54757_, boolean p_54758_) {
        if (this.shouldSpreadLiquid(p_54755_, p_54756_, p_54754_)) {
            p_54755_.getLiquidTicks().scheduleTick(p_54756_, p_54754_.getFluidState().getType(), this.getFluid().getTickDelay(p_54755_));
        }

    }

    @Override
    @Nonnull
    public BlockState updateShape(BlockState p_54723_, @Nonnull Direction p_54724_, @Nonnull BlockState p_54725_, @Nonnull LevelAccessor p_54726_, @Nonnull BlockPos p_54727_, @Nonnull BlockPos p_54728_) {
        if (p_54723_.getFluidState().isSource() || p_54725_.getFluidState().isSource()) {
            p_54726_.getLiquidTicks().scheduleTick(p_54727_, p_54723_.getFluidState().getType(), this.getFluid().getTickDelay(p_54726_));
        }

        return super.updateShape(p_54723_, p_54724_, p_54725_, p_54726_, p_54727_, p_54728_);
    }

    @Override
    public void neighborChanged(@Nonnull BlockState p_54709_, @Nonnull Level p_54710_, @Nonnull BlockPos p_54711_, @Nonnull Block p_54712_, @Nonnull BlockPos p_54713_, boolean p_54714_) {
        if (this.shouldSpreadLiquid(p_54710_, p_54711_, p_54709_)) {
            p_54710_.getLiquidTicks().scheduleTick(p_54711_, p_54709_.getFluidState().getType(), this.getFluid().getTickDelay(p_54710_));
        }

    }

    private boolean shouldSpreadLiquid(Level p_54697_, BlockPos p_54698_, BlockState p_54699_) {
        if (this.getFluid().is(FluidTags.LAVA)) {
            boolean flag = p_54697_.getBlockState(p_54698_.below()).is(Blocks.SOUL_SOIL);

            for(Direction direction : POSSIBLE_FLOW_DIRECTIONS) {
                BlockPos blockpos = p_54698_.relative(direction.getOpposite());
                if (p_54697_.getFluidState(blockpos).is(FluidTags.WATER)) {
                    Block block = p_54697_.getFluidState(p_54698_).isSource() ? Blocks.OBSIDIAN : Blocks.COBBLESTONE;
                    p_54697_.setBlockAndUpdate(p_54698_, net.minecraftforge.event.ForgeEventFactory.fireFluidPlaceBlockEvent(p_54697_, p_54698_, p_54698_, block.defaultBlockState()));
                    this.fizz(p_54697_, p_54698_);
                    return false;
                }

                if (flag && p_54697_.getBlockState(blockpos).is(Blocks.BLUE_ICE)) {
                    p_54697_.setBlockAndUpdate(p_54698_, net.minecraftforge.event.ForgeEventFactory.fireFluidPlaceBlockEvent(p_54697_, p_54698_, p_54698_, Blocks.BASALT.defaultBlockState()));
                    this.fizz(p_54697_, p_54698_);
                    return false;
                }
            }
        }

        return true;
    }

    private void fizz(LevelAccessor p_54701_, BlockPos p_54702_) {
        p_54701_.levelEvent(1501, p_54702_, 0);
    }

    @Override
    protected void createBlockStateDefinition(StateDefinition.Builder<Block, BlockState> p_54730_) {
        p_54730_.add(LEVEL);
    }

    @Override
    @Nonnull
    public ItemStack pickupBlock(@Nonnull LevelAccessor p_153772_, @Nonnull BlockPos p_153773_, @Nonnull BlockState p_153774_) {
        if (p_153774_.getValue(LEVEL) == 0) {
            p_153772_.setBlock(p_153773_, Blocks.AIR.defaultBlockState(), 11);
            return new ItemStack(this.getFluid().getBucket());
        } else {
            return ItemStack.EMPTY;
        }
    }

    // Forge start
    private final java.util.function.Supplier<? extends net.minecraft.world.level.material.Fluid> supplier;

    @Nonnull
    public FlowingFluid getFluid() {
        return (FlowingFluid)supplier.get();
    }

    private boolean fluidStateCacheInitialized = false;
    protected synchronized void initFluidStateCache() {
        if (!fluidStateCacheInitialized) {
            this.stateCache.add(getFluid().getSource(false));

            for (int i = 1; i < 8; ++i)
                this.stateCache.add(getFluid().getFlowing(8 - i, false));

            this.stateCache.add(getFluid().getFlowing(8, true));
            fluidStateCacheInitialized = true;
        }
    }

    @Override
    @Nonnull
    public Optional<SoundEvent> getPickupSound() {
        return Optional.of(SoundEvents.BUCKET_FILL);
    }
}